package com.qingzhuge.netty.client;

import com.qingzhuge.netty.common.autoconfigure.NettyServerProperties;
import com.qingzhuge.netty.common.handler.ChannelHandlerHolder;
import com.qingzhuge.netty.common.handler.HeartBeatClientHandler;
import com.qingzhuge.netty.common.message.HeartBeatMessage;
import com.qingzhuge.netty.common.message.MessageDecoder;
import com.qingzhuge.netty.common.message.MessageEncoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.SocketAddress;
import java.util.concurrent.TimeUnit;

/**
 * @author : zero.xiao
 * @description :
 * @date :2018/9/28 0028 下午 15:18
 * @modified :
 * @since : 1.0.0
 */
@ChannelHandler.Sharable
public class NettyServerHandler extends ChannelInboundHandlerAdapter
        implements TimerTask, ChannelHandlerHolder {

    private static final Logger log = LoggerFactory.getLogger(NettyServerHandler.class);

    private final Bootstrap bootstrap;
    private Timer timer;

    private volatile boolean reconnect = true;
    private int attempts;
    private volatile long refreshTime = 0L;
    private volatile boolean heartBeatCheck = false;
    private volatile Channel channel;
    private SocketAddress socketAddress;
    private NettyServerProperties properties;

    NettyServerHandler(Bootstrap boot, NettyServerProperties properties, Timer timert, SocketAddress socketAddress) {
        this.bootstrap = boot;
        this.properties = properties;
        this.timer = timert;
        this.socketAddress = socketAddress;
    }

    @Override
    public void channelActive(final ChannelHandlerContext ctx) {
        channel = ctx.channel();
        attempts = 0;
        reconnect = true;
        refreshTime = System.currentTimeMillis();
        if (!heartBeatCheck) {
            heartBeatCheck = true;
            channel.eventLoop().scheduleAtFixedRate(() -> {
                if (System.currentTimeMillis() - refreshTime > 10 * 1000L) {
                    channel.close();
                    log.info("心跳检查失败,等待重连服务器---------");
                } else {
                    log.info("心跳检查Successs");
                }
            }, 5L, 5L, TimeUnit.SECONDS);
        }
        log.info("Connects with {}.", channel);
        ctx.fireChannelActive();
    }

    /**
     * 因为链路断掉之后，会触发channelInActive方法，进行重连 重连11次后 不再重连
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        log.warn("Disconnects with {}, doReconnect = {},attemps == {}", ctx.channel(), reconnect, attempts);
        if (reconnect) {
            if (attempts < 12) {
                attempts++;
            } else {
                reconnect = false;
            }
            long timeout = 2 << attempts;
            log.info("After {} seconds client will do reconnect", timeout);
            timer.newTimeout(this, timeout, TimeUnit.SECONDS);
        }
    }

    @Override
    public void run(Timeout timeout) {

        ChannelFuture future;
        synchronized (this) {
            future = bootstrap.connect(socketAddress);
        }
        future.addListener((ChannelFutureListener) f -> {
            boolean succeed = f.isSuccess();
            log.warn("Reconnects with {}, {}.", socketAddress, succeed ? "succeed" : "failed");
            if (!succeed) {
                f.channel().pipeline().fireChannelInactive();
            }
        });
        future.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof HeartBeatMessage) {
            HeartBeatMessage heartBeat = (HeartBeatMessage) msg;
            if (properties.getHeartbeat().equals(heartBeat.getHeartbeat())) {
                refreshTime = System.currentTimeMillis();
                log.info("receive heartBeat from server, heartbeat:{}",heartBeat.getHeartbeat());
            }
        }
    }

    @Override
    public ChannelHandler[] handlers() {
        return new ChannelHandler[]{
                new MessageDecoder(),
                new MessageEncoder(),
                this,
                // 每隔30s的时间触发一次userEventTriggered的方法，并且指定IdleState的状态位是WRITER_IDLE
                new IdleStateHandler(0, 5, 0, TimeUnit.SECONDS),
                // 实现userEventTriggered方法，并在state是WRITER_IDLE的时候发送一个心跳包到sever端，告诉server端我还活着
                new HeartBeatClientHandler(properties.getHeartbeat())
        };
    }
}
